package org.hidaron.asyncdownloader.download;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;

import org.apache.http.Header;
import org.apache.http.HttpEntity;

import android.os.Message;
import android.util.Log;

import com.loopj.android.http.AsyncHttpResponseHandler;

/**
 * Hander的实现类，完成下载时要处理的任务
 * 
 * @author hidaron <hidaron@163.com>
 * 
 */
public class DownloadAsyncHttpResponseHandler extends AsyncHttpResponseHandler {
	private static final String TAG = DownloadAsyncHttpResponseHandler.class
			.getSimpleName();
	protected static final int DOWNLOAD_PAUSE_MESSAGE = 7;
	protected static final int DOWNLOAD_FINISHED_MESSAGE = 8;
	protected static final int DOWNLOAD_START_MESSAGE = 9;
	protected static final int DOWNLOAD_CANCELED_MESSAGE = 10;
	protected static final int DEFAULT_BUFFER_SIZE = 8 * 1024;

	protected Downloader mDownloader;

	private boolean isPaused;
	private boolean isCanceled;

	public DownloadAsyncHttpResponseHandler(Downloader downloader) {
		mDownloader = downloader;
	}

	@Override
	protected byte[] getResponseData(HttpEntity entity) throws IOException {
		if (entity == null) {
			return null;
		}
		InputStream inputStream = entity.getContent();
		if (mDownloader.getInfo().entityInfo.getWrittenBytesLength() == 0) {
			mDownloader.getInfo().entityInfo.setEntityLength(entity
					.getContentLength());
		}
		Log.d(TAG,
				"total content lenght "
						+ String.valueOf(mDownloader.getInfo().entityInfo
								.getEntityLength()));
		if (!Thread.currentThread().isInterrupted()) {
			sendDownloadStartMessage(
					(int) mDownloader.getInfo().entityInfo.getEntityLength(),
					(int) mDownloader.getInfo().entityInfo
							.getWrittenBytesLength());
		}
		if (null != inputStream) {
			byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
			BufferedInputStream bis = new BufferedInputStream(inputStream);
			RandomAccessFile raf = new RandomAccessFile(
					mDownloader.getTemporaryFile(), "rw");
			raf.seek(mDownloader.getInfo().entityInfo.getWrittenBytesLength());
			try {
				int length = 0;
				while ((length = bis.read(buffer)) != -1
						&& !Thread.currentThread().isInterrupted()) {
					raf.write(buffer, 0, length);
					sendProgressMessage(length,
							(int) mDownloader.getInfo().entityInfo
									.getEntityLength());
					if (isCanceled()) {
						sendDownloadCancelMessage();
						return null;
					}
					if (isPaused()) {
						byte[] writtenBytes = getWrittenBytesFormFile(mDownloader
								.getTemporaryFile());
						setEntityInfoWrittenBytesLength(writtenBytes.length);
						sendDownloadPauseMessage(writtenBytes.length);
						Log.d(TAG + "-----paused wirttenByte -----",
								String.valueOf(writtenBytes.length));
						return writtenBytes;
					}
				}
			} finally {
				if (null != raf) {
					try {
						raf.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
				if (null != bis) {
					try {
						inputStream.close();
						bis.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		}
		byte[] writtenBytes = getWrittenBytesFormFile(mDownloader
				.getTemporaryFile());
		Log.d(TAG + "-----end wirttenByte-----",
				String.valueOf(writtenBytes.length));
		Log.d(TAG + "-----total length-----", String.valueOf(mDownloader
				.getInfo().entityInfo.getEntityLength()));
		setEntityInfoWrittenBytesLength(writtenBytes.length);
		if (mDownloader.getInfo().entityInfo.getEntityLength() != 0
				&& mDownloader.getInfo().entityInfo.getEntityLength() == mDownloader
						.getInfo().entityInfo.getWrittenBytesLength()) {
			if (!Thread.currentThread().isInterrupted()) {
				sendDownloadFinishedMessage();
			}
		}
		return writtenBytes;
	}

	private byte[] getWrittenBytesFormFile(File file) {
		ByteArrayOutputStream bos = null;
		BufferedInputStream bis = null;
		byte[] writtenBytes = null;
		try {
			byte[] buffer = new byte[1024];
			bos = new ByteArrayOutputStream();
			bis = new BufferedInputStream(new FileInputStream(file));
			int length = 0;
			while ((length = bis.read(buffer)) != -1) {
				bos.write(buffer, 0, length);
			}
			if (null != bos) {
				writtenBytes = bos.toByteArray();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (null != bos) {
				try {
					bos.close();
				} catch (Exception e2) {
					e2.printStackTrace();
				}
			}
			if (null != bis) {
				try {
					bis.close();
				} catch (Exception e2) {
					e2.printStackTrace();
				}
			}
		}
		return writtenBytes;
	}

	@Override
	public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
		if (null != mDownloader.getOnDownloadListener()) {
			mDownloader.getOnDownloadListener().onSuccess(statusCode,
					responseBody);
		}
	}

	@Override
	public void onFailure(int statusCode, Header[] headers,
			byte[] responseBody, Throwable error) {
		setDownloadStatus(DownloadStatus.FAILURE);
		byte[] writtenBytes = getWrittenBytesFormFile(mDownloader
				.getTemporaryFile());
		Log.d(TAG,
				"download failure content length "
						+ String.valueOf(writtenBytes.length));
		setEntityInfoWrittenBytesLength(writtenBytes.length);
		if (null != mDownloader.getOnDownloadListener()) {
			mDownloader.getOnDownloadListener().onFailure(statusCode,
					responseBody, error);
		}
	}

	@Override
	public void onProgress(int bytesWritten, int totalSize) {
		super.onProgress(bytesWritten, totalSize);
		if (mDownloader.getInfo().getStatus() != DownloadStatus.DOWNLOADING) {
			setDownloadStatus(DownloadStatus.DOWNLOADING);
		}
		if (null != mDownloader.getOnDownloadListener()) {
			mDownloader.getOnDownloadListener().onProgress(bytesWritten,
					totalSize);
		}
	}

	protected void onDownloadStart(int conentLength, int writtenBytes) {
		Log.d(TAG, "download start");
		setDownloadStatus(DownloadStatus.START);
		// 记录第一次任务的下载时间
		if (writtenBytes == 0) {
			mDownloader.getInfo().setDownloadFirstTime(
					System.currentTimeMillis());
		}
		if (null != mDownloader.getOnDownloadListener()) {
			mDownloader.getOnDownloadListener().onStart(conentLength,
					writtenBytes, mDownloader.getInfo().getDownloadFirstTime());
		}
	}

	protected void onDownloadPaused(int writtenBytesLength) {
		Log.d(TAG, "download pasused");
		setDownloadStatus(DownloadStatus.PAUSED);
		if (null != mDownloader.getOnDownloadListener()) {
			mDownloader.getOnDownloadListener().onPaused(writtenBytesLength);
		}
	}

	public void onDownloadFinished() {
		Log.d(TAG, "download finished");
		setDownloadStatus(DownloadStatus.FINISHED);
		if (null != mDownloader.getOnDownloadListener()) {
			try {
				mDownloader.getOnDownloadListener().onFinished(
						mDownloader.getTemporaryFile().getCanonicalPath());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	public void OnDownloadCanceled() {
		Log.d(TAG, "download canceled");
		setDownloadStatus(DownloadStatus.CANCELED);
		mDownloader.deleteTemporaryFile();
		if (null != mDownloader.getOnDownloadListener()) {
			mDownloader.getOnDownloadListener().onCanceled();
		}
	}

	@Override
	protected void handleMessage(Message message) {
		super.handleMessage(message);
		Object[] response;
		switch (message.what) {
		case DOWNLOAD_PAUSE_MESSAGE:
			response = (Object[]) message.obj;
			onDownloadPaused((Integer) response[0]);
			break;
		case DOWNLOAD_FINISHED_MESSAGE:
			onDownloadFinished();
			break;
		case DOWNLOAD_START_MESSAGE:
			response = (Object[]) message.obj;
			onDownloadStart((Integer) response[0], (Integer) response[1]);
			break;
		case DOWNLOAD_CANCELED_MESSAGE:
			OnDownloadCanceled();
		}
	}

	final public void sendDownloadPauseMessage(int writtenBytesLength) {
		sendMessage(obtainMessage(DOWNLOAD_PAUSE_MESSAGE,
				new Object[] { writtenBytesLength }));
	}

	final public void sendDownloadFinishedMessage() {
		sendMessage(obtainMessage(DOWNLOAD_FINISHED_MESSAGE, null));
	}

	final public void sendDownloadCancelMessage() {
		sendMessage(obtainMessage(DOWNLOAD_CANCELED_MESSAGE, null));
	}

	final public void sendDownloadStartMessage(int contentLength,
			int writtenBytes) {
		sendMessage(obtainMessage(DOWNLOAD_START_MESSAGE, new Object[] {
				contentLength, writtenBytes }));
	}

	public synchronized void setDownloadStatus(DownloadStatus status) {
		mDownloader.setDownloadStatus(status);
	}

	public boolean isPaused() {
		return isPaused;
	}

	public boolean isCanceled() {
		return isCanceled;
	}

	public synchronized void setPause(boolean isPaused) {
		this.isPaused = isPaused;
	}

	public synchronized void setCanceled(boolean isCanceled) {
		this.isCanceled = isCanceled;
	}

	private synchronized void setEntityInfoWrittenBytesLength(long length) {
		mDownloader.getInfo().entityInfo.setWrittenBytesLength(length);
	}

}
